package org.dea.util;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;

import de.gulden.util.javasource.Class;
import de.gulden.util.javasource.Code;
import de.gulden.util.javasource.Constructor;
import de.gulden.util.javasource.Exception;
import de.gulden.util.javasource.Field;
import de.gulden.util.javasource.ImportClass;
import de.gulden.util.javasource.Member;
import de.gulden.util.javasource.MemberExecutable;
import de.gulden.util.javasource.Method;
import de.gulden.util.javasource.NamedIterator;
import de.gulden.util.javasource.Package;
import de.gulden.util.javasource.Parameter;
import de.gulden.util.javasource.SourceParser;
import de.gulden.util.javasource.Type;
import de.gulden.util.javasource.jjt.ParseException;
import de.gulden.util.javasource.sourclet.standard.StandardSourclet;

public class SourceUtil {

	public static Class loadClassSource(File sourceDir, String name) throws IOException, ParseException {
		Package packageFacade;
		Class baseClass;
		packageFacade = SourceParser.parse( sourceDir, null );
		baseClass = packageFacade.findClass( name );
		return baseClass;
	}

	public static void changeClassName(Class baseClass, String newClassName) {
		baseClass.setName(newClassName);
		Vector v = getConstructors(baseClass);
		for (Iterator iter = v.iterator(); iter.hasNext();) {
			Constructor constr = (Constructor) iter.next();
			constr.setName(newClassName);
		}
	}
	
	public static void addExcecption( Method method, String exceptionName ){
		NamedIterator exceptions = method.getExceptions();
		Exception exception = new Exception( method );
		exception.setName( exceptionName );
		exceptions.add( exception );
	}
	
	public static void addExcecption( Constructor constr, String exceptionName ){
		NamedIterator exceptions = constr.getExceptions();
		Exception exception = new Exception( constr );
		exception.setName( exceptionName );
		exceptions.add( exception );
	}
	
	
	public static File generateSourceFile( File sourceDir, Class clazz ) throws IOException {

		String classname = clazz.getUnqualifiedName();
		String packPath = clazz.getPackage().getName().replace(".", "/");
		File file = new File( new File(sourceDir.getAbsolutePath()+"/"+packPath), classname + ".java" );
		FileOutputStream f = new FileOutputStream( file );
		BufferedOutputStream buf = new BufferedOutputStream( f );
		StandardSourclet sourclet = new StandardSourclet();
		sourclet.buildSource( buf, clazz );
		buf.flush();
		buf.close();

		return file;
	}
	
	public static void addInterfaces(Class baseFacadeClass, String newIF ){
		Enumeration oldIFs = baseFacadeClass.getInterfaceNames();
		LinkedList<String> list = new LinkedList<String>();
		while (oldIFs.hasMoreElements()) {
			String aIF = (String) oldIFs.nextElement();
			list.add(aIF);
		}
		list.add(newIF);
		String[] interfaces = new String[list.size()];
		list.toArray(interfaces);
		baseFacadeClass.setInterfaceNames(interfaces);
	}
	
	/**
	 * 
	 */
	public static void addImport(Class baseFacadeClass, String pkg, String clazz ) {

		NamedIterator imports = baseFacadeClass.getImports();
		imports.add( createImportClass(pkg, clazz));
	}
	
	public static Class extractInterface(Class baseClass, String ifName) {
		Class rmiIF = new Class();
		rmiIF.setPackage(baseClass.getPackage());
		rmiIF.setName(ifName);
		SourceUtil.setInterface(rmiIF);
		Vector methods = SourceUtil.getMethods(baseClass);
		for (Iterator iter = methods.iterator(); iter.hasNext();) {
			Method method = (Method) iter.next();
			method.setCode(null);
			rmiIF.getAllMembers().add(method);
		}
		SourceUtil.copyImports(baseClass, rmiIF);
		return rmiIF;
	}
	
	public static void copyImports( Class baseFacadeClass, Class targetClass){
		NamedIterator imports = baseFacadeClass.getImports();
		NamedIterator importsTgt = targetClass.getImports();
		importsTgt.add(imports);
	}
	
	public static void addExceptionAllMethods( Class baseFacadeClass, String exceptionName ){
		Vector methods = SourceUtil.getMethods(baseFacadeClass);
		for (Iterator iter = methods.iterator(); iter.hasNext();) {
			Method method = (Method) iter.next();
			SourceUtil.addExcecption(method, exceptionName);
		}
		Vector constructors = SourceUtil.getConstructors(baseFacadeClass);
		for (Iterator iter = constructors.iterator(); iter.hasNext();) {
			Constructor constructor = (Constructor) iter.next();
			SourceUtil.addExcecption(constructor, exceptionName);
		}
	}
	
	private static ImportClass createImportClass( String packageName, String className ) {

		Package pack = new Package();
		pack.setName( packageName );
		ImportClass importClass = new ImportClass( pack );
		importClass.setName( packageName + "." + className );

		return importClass;
	}
	
	public static void changeSuperClass( Class baseFacadeClass, String superClass ){
		baseFacadeClass.setSuperclassName(superClass);
	}
	
	public static Vector getMethods(Class baseFacadeClass) {

		NamedIterator members = baseFacadeClass.getAllMembers();
		Vector<Member> methods = new Vector<Member>();
		while ( members.hasMore() ) {
			Member m = (Member) members.next();
			if ( (m instanceof Method) && (Modifier.isPublic( m.getModifier() )) ) {
				methods.add( m );
			}
		}
		return methods;
	}
	
	public static void setInterface(Class baseFacadeClass) {
		baseFacadeClass.setInterface(true);
		baseFacadeClass.setModifier( Modifier.PUBLIC );
	}
	
	public static void addParameter(String parameterName, String parameterType, Method method) {
		NamedIterator parameters = method.getParameters();
		parameters.add( SourceUtil.createParameter( method, parameterName, parameterType, 0 ) );
	}
	
	public static void addParameter(String parameterName, String parameterType, Constructor constructor) {
		NamedIterator parameters = constructor.getParameters();
		parameters.add( SourceUtil.createParameter( constructor, parameterName, parameterType, 0 ) );
	}
	
	public static void addField(Class baseFacadeClass, String name, String type ){
		Field field = new Field( baseFacadeClass );
		field.setName( "distributedComponentsList" );
		field.setType( createType( field, type, 0 ) );
		Code code = new Code();
		field.setCode( code );
		baseFacadeClass.getAllMembers().add( field );
		
	}

	
	public static Vector getConstructors(Class baseFacadeClass) {

		NamedIterator members = baseFacadeClass.getAllMembers();
		Vector<Member> methods = new Vector<Member>();
		while ( members.hasMore() ) {
			Member m = (Member) members.next();
			if ( (m instanceof Constructor) && (Modifier.isPublic( m.getModifier() )) ) {
				methods.add( m );
			}
		}
		return methods;
	}
	
	public static Constructor getConstructor(Class baseFacadeClass, String[] paramTypes ){
		Vector constructors = SourceUtil.getConstructors(baseFacadeClass);
		for (Iterator iter = constructors.iterator(); iter.hasNext();) {
			Constructor constructor = (Constructor) iter.next();
			if( constructor.myParameter.size() == paramTypes.length ){
				if( paramTypes.length == 0 )
					return constructor;
				for (int i = 0; i < paramTypes.length; i++) {
					Parameter param = (Parameter) constructor.myParameter.get(i);
					if( !param.getType().getFullTypeName().equals(paramTypes[i]) )
						break;
					else if( i == paramTypes.length-1 )
						return constructor;
				}
			}
		}
		return null;
	}
	
	public static Type createType( Member member, String name, int dimension ) {

		Type type = new Type( member );
		type.setDimension( dimension );
		type.setTypeName( name );
		return type;
	}
	
	public static Parameter createParameter( Member member, String name, String type, int dimension ) {

		Parameter parameter = new Parameter( (MemberExecutable) member );
		parameter.setName( name );
		parameter.setType( createType( member, type, dimension ) );

		return parameter;
	}
	
	public static void addMethod( Class baseFacadeClass, int modifier, String name, String[] paramsName, String[] paramsType, String returnType, String codeRaw ) {

		// Create object Method
		Method method = new Method( baseFacadeClass );
		method.setName( name );
		method.setModifier( modifier );

		// Set getFacadeInterface parameters
		NamedIterator parameters = method.getParameters();
		for (int i = 0; i < paramsType.length; i++) {
			parameters.add( createParameter( method, paramsName[i], paramsType[i], 0 ) );
		}

		// Set return type
		Type type = new Type( method );
		type.setDimension( 0 );
		type.setTypeName( returnType );
		method.setType( type );

		// Set getFacadeInterface method body
		Code code = new Code();
		code.setRaw( codeRaw );
		method.setCode( code );

		// Add object method to the class
		baseFacadeClass.getAllMembers().add( method );
	}
	
	
	
	
	

}
